package com.ewei.web.common.json.resolver;

import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import com.ewei.web.common.json.JsonUtils;
//自定义类型转换类，主要还是解决RequestBody不适用表单提交的问题
public class JsonModelResolver implements HandlerMethodArgumentResolver{
	private static Logger logger = LogManager.getLogger(JsonModelResolver.class);
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(JsonModel.class);
	}
	private Map<String,Object> transerFormMap(Map<String,String[]> formMap){//对request的参数表进行处理
		if(null != formMap){
			Set<Entry<String,String[]>> entrySet = formMap.entrySet();
			Map<String,Object> transerFormMap = new HashMap<String, Object>(); 
			for(Entry<String,String[]> entry:entrySet){
				String[] value = entry.getValue();
				String key = entry.getKey();
				if(value.length == 1 &&!StringUtils.isEmpty(value[0])){
					transerFormMap.put(key, value[0]);
				}else if(value.length>1){
					transerFormMap.put(key, value);
				}
			}
			return transerFormMap;
		}
		return null;
	}
	private String  transerString(Map<String,String[]> formMap){//将request的参数表输出成字符串
		StringBuilder builder = new StringBuilder();
		Set<Entry<String,String[]>> entrySet = formMap.entrySet();
		for(Entry<String,String[]> entry:entrySet){
			String[] value = entry.getValue();
			String key = entry.getKey();
			if(null!=key){
				builder.append(key);
			}
			if(value!=null){
				for(int i = 0;i<value.length;i++){
					builder.append(value[i]);
				}
			}
		}
		return builder.toString();
	}
	private String getParameterName(MethodParameter parameter){
		JsonModel json = parameter.getParameterAnnotation(JsonModel.class);
		String value =json.value();
		if(StringUtils.isEmpty(value)){
			String name = parameter.getParameterType().getSimpleName();
			char[] nameChars = name.toCharArray();
			if(nameChars.length>1&&Character.isLowerCase(nameChars[2])){
				nameChars[0] = Character.toLowerCase(nameChars[0]);
			}
			value = new String(nameChars);
		}
		return value;
	}
	public Object resolveArgument(MethodParameter parameter,
			ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
			WebDataBinderFactory binderFactory) throws Exception {
		logger.trace("enter method: "+parameter.getMethod().getName());
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		Class<?> clzType = parameter.getParameterType();
		String parameterName = getParameterName(parameter);
		Object result = null;
		if(ClassUtils.isAssignable(JsonModelMap.class, clzType)){//这里对GET方法提交的json数据做特殊处理
			JsonModelMap jsonModelMap = new JsonModelMap();
			if(!StringUtils.pathEquals(request.getMethod(),"GET")){
				Map<String,Object> jsonMap = JsonUtils.fromJson(request.getInputStream(), Map.class);
				if(null == jsonMap){
					jsonMap = new HashMap<String,Object>();
				}
				jsonModelMap.setMap(jsonMap);
				Map<String, Object> formMap = transerFormMap(request.getParameterMap());
				if(null != formMap){
					jsonModelMap.merge(formMap);
				}
			}else{
				Map<String,Object> jsonMap = JsonUtils.fromJson(transerString(request.getParameterMap()),Map.class);
				jsonModelMap.setMap(jsonMap);
			}
			result = jsonModelMap;
		}else{
			if(ClassUtils.isAssignable(List.class, clzType)){//获得List泛型的class对象
				ParameterizedType parameterizedType = 
						(ParameterizedType) parameter.getGenericParameterType();
				Class<?> clz = (Class<?>)parameterizedType.getActualTypeArguments()[0];
				result = JsonUtils.fromJson(request.getInputStream(),clz);
				if(null == result){
					StringBuilder json = new StringBuilder("[");
					String[] value = request.getParameterMap().get(parameterName);
					if(null!=value&&(value.length > 1||!StringUtils.isEmpty(value[0]))){
						for(int i = 0;i<value.length;i++){
							json.append(value[i]);
							if(i != value.length-1){
								json.append(",");
							}
							else json.append("]");
						}
						result = JsonUtils.fromJson(json.toString(), clz);
					}
				}
			}else{
				result = JsonUtils.fromJson(request.getInputStream(), clzType);
				if(null == result){
					if(!StringUtils.pathEquals(request.getMethod(),"GET")){
						result = JsonUtils.fromJson(
								JsonUtils.toJson(transerFormMap(request.getParameterMap()),null),clzType);
					}else{
						result = JsonUtils.fromJson(transerString(request.getParameterMap()), clzType);
					}
				}
			}
		}
		logger.trace("Resolved Parameter: "+parameter.getParameterName());
		//绑定参数,为了validate数据
		WebDataBinder binder = binderFactory.createBinder(webRequest,result,parameterName);
		if(binder.getTarget()!=null){
			validateIfApplicable(binder,parameter);
			boolean isBindExceptionRequired = isBindExceptionRequired(binder, parameter);
			if(!isBindExceptionRequired){
				mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX.concat(parameterName), binder.getBindingResult());
			}
			if(isBindExceptionRequired&&
					binder.getBindingResult().hasErrors()){
				throw new BindException(binder.getBindingResult());
			}
		}
		logger.trace("Bindinged Parameters finished...");
		return result;
	}
	protected boolean isBindExceptionRequired(WebDataBinder binder,MethodParameter parameter){
		int i = parameter.getParameterIndex();
		Class<?>[] paramTypes = parameter.getMethod().getParameterTypes();
		boolean hasBindingResult = paramTypes.length>(i + 1) && Errors.class.isAssignableFrom(paramTypes[i+1]);
		return !hasBindingResult;
	}
	protected void validateIfApplicable(WebDataBinder binder,
			MethodParameter parameter) {
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation annot : annotations) {
			Validated validatedAnn = AnnotationUtils.getAnnotation(annot,
					Validated.class);
			if (validatedAnn!=null||annot.annotationType().getSimpleName().startsWith("Valid")) {
				Object hints =validatedAnn != null? validatedAnn.value():AnnotationUtils.getValue(annot);
				binder.validate(hints instanceof Object[] ? (Object[]) hints: new Object[] {hints});
				break;
			}
		}
	}
}
